#![allow(unused_extern_crates)]
extern crate serde_ignored;
extern crate tokio_core;
extern crate native_tls;
extern crate hyper_tls;
extern crate openssl;
extern crate mime;
extern crate chrono;
extern crate percent_encoding;
extern crate url;
extern crate multipart;

use std::sync::Arc;
use std::marker::PhantomData;
use futures::{Future, future, Stream, stream};
use hyper;
use hyper::{Request, Response, Error, StatusCode};
use hyper::header::{Headers, ContentType};
use self::url::form_urlencoded;
use mimetypes;
use self::multipart::server::Multipart;
use self::multipart::server::save::SaveResult;
use std::fs;
use serde_json;

#[allow(unused_imports)]
use std::collections::{HashMap, BTreeMap};
#[allow(unused_imports)]
use swagger;
use std::io;

#[allow(unused_imports)]
use std::collections::BTreeSet;

pub use swagger::auth::Authorization;
use swagger::{ApiError, XSpanId, XSpanIdString, Has, RequestParser};
use swagger::auth::Scopes;
use swagger::headers::SafeHeaders;

use {Api,
     MultipartRequestPostResponse
     };
#[allow(unused_imports)]
use models;

pub mod context;

header! { (Warning, "Warning") => [String] }

mod paths {
    extern crate regex;

    lazy_static! {
        pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(vec![
            r"^/multipart_request$"
        ]).unwrap();
    }
    pub static ID_MULTIPART_REQUEST: usize = 0;
}

pub struct NewService<T, C> {
    api_impl: Arc<T>,
    marker: PhantomData<C>,
}

impl<T, C> NewService<T, C>
where
    T: Api<C> + Clone + 'static,
    C: Has<XSpanIdString>  + 'static
{
    pub fn new<U: Into<Arc<T>>>(api_impl: U) -> NewService<T, C> {
        NewService{api_impl: api_impl.into(), marker: PhantomData}
    }
}

impl<T, C> hyper::server::NewService for NewService<T, C>
where
    T: Api<C> + Clone + 'static,
    C: Has<XSpanIdString>  + 'static
{
    type Request = (Request, C);
    type Response = Response;
    type Error = Error;
    type Instance = Service<T, C>;

    fn new_service(&self) -> Result<Self::Instance, io::Error> {
        Ok(Service::new(self.api_impl.clone()))
    }
}

pub struct Service<T, C> {
    api_impl: Arc<T>,
    marker: PhantomData<C>,
}

impl<T, C> Service<T, C>
where
    T: Api<C> + Clone + 'static,
    C: Has<XSpanIdString>  + 'static {
    pub fn new<U: Into<Arc<T>>>(api_impl: U) -> Service<T, C> {
        Service{api_impl: api_impl.into(), marker: PhantomData}
    }
}

impl<T, C> hyper::server::Service for Service<T, C>
where
    T: Api<C> + Clone + 'static,
    C: Has<XSpanIdString>  + 'static
{
    type Request = (Request, C);
    type Response = Response;
    type Error = Error;
    type Future = Box<dyn Future<Item=Response, Error=Error>>;

    fn call(&self, (req, mut context): Self::Request) -> Self::Future {
        let api_impl = self.api_impl.clone();
        let (method, uri, _, headers, body) = req.deconstruct();
        let path = paths::GLOBAL_REGEX_SET.matches(uri.path());

        // This match statement is duplicated below in `parse_operation_id()`.
        // Please update both places if changing how this code is autogenerated.
        match &method {

            // MultipartRequestPost - POST /multipart_request
            &hyper::Method::Post if path.matched(paths::ID_MULTIPART_REQUEST) => {
                let boundary = match multipart_boundary(&headers) {
                    Some(boundary) => boundary,
                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body("Couldn't find valid multipart body"))),
                };
                // Form Body parameters (note that non-required body parameters will ignore garbage
                // values, rather than causing a 400 response). Produce warning header and logs for
                // any unused fields.
                Box::new(body.concat2()
                    .then(move |result| -> Box<dyn Future<Item=Response, Error=Error>> {
                        match result {
                            Ok(body) => {
                                // Read Form Parameters from body
                                let mut entries = match Multipart::with_body(&body.to_vec()[..], boundary).save().temp() {
                                    SaveResult::Full(entries) => {
                                        entries
                                    },
                                    _ => {
                                        return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Unable to process all message parts"))))
                                    },
                                };
                
                                
                                let file_string_field = entries.files.remove("string_field");
                                let param_string_field = match file_string_field {
                                    Some(file) => {
                                        let path = &file[0].path;
                                        let string_field_str = fs::read_to_string(path).expect("Reading saved String should never fail");
                                        let string_field_model: String = match serde_json::from_str(&string_field_str) {
                                            Ok(model) => model,
                                            Err(e) => {
                                                return Box::new(future::ok(
                                                    Response::new()
                                                    .with_status(StatusCode::BadRequest)
                                                    .with_body(format!("string_field data does not match API definition: {}", e))))
                                            }
                                        };
                                        string_field_model
                                    }
                                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Missing required form parameter string_field")))),
                                };
                                

                                
                                let file_optional_string_field = entries.files.remove("optional_string_field");
                                let param_optional_string_field = match file_optional_string_field {
                                   Some(file) => {
                                        let path = &file[0].path;
                                        let optional_string_field_str = fs::read_to_string(path).unwrap();
                                        let optional_string_field_model: String = serde_json::from_str(&optional_string_field_str).expect("Impossible to fail to serialise");
                                        Some(optional_string_field_model)
                                    }
                                    None => None,
                                };
                                

                                
                                let file_object_field = entries.files.remove("object_field");
                                let param_object_field = match file_object_field {
                                   Some(file) => {
                                        let path = &file[0].path;
                                        let object_field_str = fs::read_to_string(path).unwrap();
                                        let object_field_model: models::MultipartRequestObjectField = serde_json::from_str(&object_field_str).expect("Impossible to fail to serialise");
                                        Some(object_field_model)
                                    }
                                    None => None,
                                };
                                

                                let file_binary_field = entries.files.remove("binary_field");
                                let param_binary_field = match file_binary_field {
                                    Some(file) => {
                                        let path = &file[0].path;
                                        let binary_field_str = fs::read_to_string(path).unwrap();
                                        swagger::ByteArray(binary_field_str.as_bytes().to_vec())
                                    }
                                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Missing required form parameter binary_field")))),
                                };
                                
                                Box::new(api_impl.multipart_request_post(param_string_field, param_binary_field, param_optional_string_field, param_object_field, &context)
                                    .then(move |result| {
                                        let mut response = Response::new();
                                        response.headers_mut().set(XSpanId((&context as &dyn Has<XSpanIdString>).get().0.to_string()));

                                        match result {
                                            Ok(rsp) => match rsp {
                                                MultipartRequestPostResponse::OK


                                                => {
                                                    response.set_status(StatusCode::try_from(201).unwrap());

                                                },
                                            },
                                            Err(_) => {
                                                // Application code returned an error. This should not happen, as the implementation should
                                                // return a valid response.
                                                response.set_status(StatusCode::InternalServerError);
                                                response.set_body("An internal error occurred");
                                            },
                                        }

                                        future::ok(response)
                                    }
                                ))
                                as Box<dyn Future<Item=Response, Error=Error>>
                            },
                            Err(e) => Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't read multipart body")))),
                        }
                    })
                )
            },

            _ => Box::new(future::ok(Response::new().with_status(StatusCode::NotFound))) as Box<dyn Future<Item=Response, Error=Error>>,
        }
    }
}

impl<T, C> Clone for Service<T, C>
{
    fn clone(&self) -> Self {
        Service {
            api_impl: self.api_impl.clone(),
            marker: self.marker.clone(),
        }
    }
}

/// Utility function to get the multipart boundary marker (if any) from the Headers.
fn multipart_boundary(headers: &Headers) -> Option<String> {
    headers.safe_get::<ContentType>().and_then(|content_type| {
        let ContentType(mime) = content_type;
        if mime.type_() == hyper::mime::MULTIPART && mime.subtype() == hyper::mime::FORM_DATA {
            mime.get_param(hyper::mime::BOUNDARY).map(|x| x.as_str().to_string())
        } else {
            None
        }
    })
}

/// Request parser for `Api`.
pub struct ApiRequestParser;
impl RequestParser for ApiRequestParser {
    fn parse_operation_id(request: &Request) -> Result<&'static str, ()> {
        let path = paths::GLOBAL_REGEX_SET.matches(request.uri().path());
        match request.method() {

            // MultipartRequestPost - POST /multipart_request
            &hyper::Method::Post if path.matched(paths::ID_MULTIPART_REQUEST) => Ok("MultipartRequestPost"),
            _ => Err(()),
        }
    }
}
